﻿using System;
using System.Collections.Generic;
using System.Linq;

using Microsoft.Xna.Framework;

namespace WorldOnFire
{
    public class CObjectManager
    {
        #region Object Manager Data

        private CCollisionManager m_CollisionManager;
        private CWaveManager m_WaveManager;

        private List<ABCObject> m_ObjectList;
        private List<ABCObject> m_StaticList;
        private List<ABCMovingObject> m_MovingList;
        
        private List<ABCEnemy> m_EnemyList;
        private List<ABCTurret> m_TurretList;
        private List<ABCProjectile> m_ProjectileList;

        private static readonly int[] OBJECT_COUNT = 
        {
            2,                      // HOMEWORLD
            10,                     // SATELLITE
            10,                     // TARGET
            42,                     // SHIELD
            5,                      // ENEMY_WARP_GATE
            250,                    // ENEMY_BOMBER
            100,                    // ENEMY_DETONATOR
            25,                     // ENEMY_GUARDIAN
            100,                    // ENEMY_FRIGATE
            20,                     // ENEMY_DESTROYER
            20,                     // ENEMY_CRUISER
            10,                     // ENEMY_BATTLESHIP
            20,                     // ENEMY_CARRIER
            5,                      // ENEMY_JUGGERNAUT
            25,                     // TURRET_GATLING
            25,                     // TURRET_FLAK
            25,                     // TURRET_CANNON
            25,                     // TURRET_LASER
            25,                     // TURRET_EMP
            25,                     // TURRET_GRAVITY_WELL
            25,                     // TURRET_BLACK_HOLE
            25,                     // TURRET_RELAY
            500,                    // PROJECTILE_BULLET
            125,                    // PROJECTILE_FLAK
            100,                    // PROJECTILE_SHELL
            50,                     // PROJECTILE_LASER
            100,                    // PROJECTILE_EMP
            100,                    // PROJECTILE_BLACK_HOLE
            50,                     // PROJECTILE_RELAY
            300,                    // PROJECTILE_BOMB
            400,                    // PROJECTILE_PLASMA
            250                     // PROJECTILE_MISSILE
        };              

        //private CMiningProbe m_MiningProbes;
        //private CMiningPlatform m_MiningPlatforms;

        #endregion Object Manager Data

        #region Accessors

        public ABCEnemy GetEnemy(ABCEnemy.eEnemyType _eType)
        {
            return m_EnemyList[(int)_eType];
        }
        
        public ABCTurret GetTurret(ABCTurret.eTurretType _eType)
        {
            return m_TurretList[(int)_eType];
        }

        public ABCProjectile GetProjectile(ABCProjectile.eProjectileType _eType)
        {
            return m_ProjectileList[(int)_eType];
        }

        public CSatellite GetCelestialBody()
        {
            return (CSatellite)m_ObjectList[(int)ABCObject.eObjectType.SATELLITE];
        }

        #endregion Accessors

        #region Initialization

        public void Initialize(CRenderingManager _RenderingManager)
        {
            // Initialize lists
            m_ObjectList = new List<ABCObject>();
            m_StaticList = new List<ABCObject>();
            m_MovingList = new List<ABCMovingObject>();

            m_EnemyList = new List<ABCEnemy>();
            m_TurretList = new List<ABCTurret>();
            m_ProjectileList = new List<ABCProjectile>();

            // Initialize collision manager
            m_CollisionManager = new CCollisionManager();
            m_CollisionManager.Initialize(m_ObjectList, m_MovingList);

            // Set the object manager for the objects
            ABCObject.SetObjectManager(this);

            // Create the objects
            // NOTE: Objects must be added in order according to the ABCObject.eObjectType enumeration
 
            // Create the world
            this.AddObject(new CHomeworld());
            this.AddObject(new CSatellite());
            this.AddObject(new CTarget());
            this.AddObject(new CShield());

            // Create the enemies
            this.AddObject(new CWarpGate());
            this.AddObject(new CBomber());
            this.AddObject(new CDetonator());
            this.AddObject(new CGuardian());
            this.AddObject(new CFrigate());
            this.AddObject(new CDestroyer());
            this.AddObject(new CCruiser());
            this.AddObject(new CBattleship());
            this.AddObject(new CCarrier());
            this.AddObject(new CJuggernaut());

            // Create the turrets
            this.AddObject(new CTurretRepeater());
            this.AddObject(new CTurretFlak());
            this.AddObject(new CTurretCannon());
            this.AddObject(new CTurretLaser());
            this.AddObject(new CTurretEMP());
            this.AddObject(new CTurretGravityWell());
            this.AddObject(new CTurretBlackHole());
            this.AddObject(new CTurretRelay());

            // Create the projectiles
            this.AddObject(new CBullet());
            this.AddObject(new CFlak());
            this.AddObject(new CShell());
            this.AddObject(new CLaser());
            this.AddObject(new CEMP());
            this.AddObject(new CBlackHole());
            this.AddObject(new CRelay());
            this.AddObject(new CBomb());
            this.AddObject(new CPlasma());
            this.AddObject(new CMissile());

            // Loop through each object
            for (int iObject = 0; iObject < m_ObjectList.Count; ++iObject)
            {
                // Initialize each with its corresponding number of objects
                m_ObjectList[iObject].Initialize(OBJECT_COUNT[iObject]);

                // Add the object to the rendering manager
                _RenderingManager.AddObject(m_ObjectList[iObject]);
            }

            // Initialize the wave manager
            m_WaveManager = new CWaveManager();
            m_WaveManager.Initialize(this, m_EnemyList, (CWarpGate)m_ObjectList[(int)ABCObject.eObjectType.ENEMY_WARP_GATE]);


            // TODO: KILL
            m_ObjectList[(int)ABCObject.eObjectType.HOMEWORLD].ScheduleForActivation(0);

            CRandom Ron = new CRandom();

            // TODO: MOVE THIS SHIT ELSEWHERE
            for (int iSatellite = 0; iSatellite < OBJECT_COUNT[(int)ABCObject.eObjectType.SATELLITE]; ++iSatellite)
            {
                m_ObjectList[(int)ABCObject.eObjectType.SATELLITE].CreateObject(iSatellite);
                m_ObjectList[(int)ABCObject.eObjectType.SATELLITE].ScheduleForActivation(iSatellite);

                m_ObjectList[(int)ABCObject.eObjectType.SATELLITE].SetTransform(iSatellite,
                    Matrix.CreateTranslation(new Vector3(Ron.NextFloat(-500.0f, 500.0f), 0.0f, Ron.NextFloat(-500.0f, 500.0f))));
                    //Matrix.CreateTranslation(new Vector3(500.0f, 0.0f, -250.0f)));
            }

            // TODO: RAGEEEEE TARGET
            m_ObjectList[(int)ABCObject.eObjectType.TARGET].CreateObject(0);
            m_ObjectList[(int)ABCObject.eObjectType.TARGET].ScheduleForActivation(0);
            ((CTarget)m_ObjectList[(int)ABCObject.eObjectType.TARGET]).SetValue(0, 100.0f);
            ((CTarget)m_ObjectList[(int)ABCObject.eObjectType.TARGET]).m_vPosition[0] = Vector3.Zero;

            // TODO: FUUUU AI MANAGER. JESUS
            //for (int iObject = 0; iObject < OBJECT_COUNT[(int)ABCObject.eObjectType.ENEMY_WARP_GATE]; ++iObject)
            //{
            //    m_ObjectList[(int)ABCObject.eObjectType.ENEMY_WARP_GATE].ScheduleForActivation(iObject);

            //}

            // TODO: Yes. Another one.
            //for (int iEnemyType = 0; iEnemyType < (int)ABCEnemy.eEnemyType.COUNT; ++iEnemyType)
            //{
            //    int nTypeIndex = (int)ABCObject.eObjectType.ENEMY_BOMBER + iEnemyType;

            //    for (int iEnemy = 0; iEnemy < (int)(0.5 * OBJECT_COUNT[nTypeIndex]); ++iEnemy)
            //    {
            //        m_ObjectList[nTypeIndex].ScheduleForActivation(iEnemy);

            //        m_ObjectList[nTypeIndex].SetTransform(iEnemy, Matrix.CreateTranslation(
            //            new Vector3(Ron.NextFloat(-1000.0f, 1000.0f), 0.0f, Ron.NextFloat(-1000.0f, 1000.0f))));
            //    }
            //}
        }

        public void Shutdown()
        {
            // Clear all lists
            m_ObjectList.Clear();
            m_MovingList.Clear();
            m_StaticList.Clear();
        }

        private void AddObject(ABCObject _Object)
        {
            // Add the object to the main list
            m_ObjectList.Add(_Object);

            // If the object is a moving object
            if (_Object is ABCMovingObject)
            {
                // Add it to the moving object list
                m_MovingList.Add((ABCMovingObject)_Object);

                // Add it to any other lists
                if (_Object is ABCEnemy)
                {
                    m_EnemyList.Add((ABCEnemy)_Object);
                }
                else if (_Object is ABCProjectile)
                {
                    m_ProjectileList.Add((ABCProjectile)_Object);
                }
            }
            // If the object is a static object
            else
            {
                // Add it to the static object list
                m_StaticList.Add(_Object);

                // Add it to any other lists
                if (_Object is ABCTurret)
                {
                    m_TurretList.Add((ABCTurret)_Object);
                }

                // Set the enemy's target object
                if (_Object is CTarget)
                {
                    ABCEnemy.SetTarget((CTarget)_Object);
                }
            }
        }

        #endregion Initialization

        #region Update

        public void Update(float _fDeltaTime)
        {
            // Update the wave manager
            m_WaveManager.Update(_fDeltaTime);

            // Update each object
            for (int iObject = 0; iObject < m_ObjectList.Count; ++iObject)
            {
                m_ObjectList[iObject].UpdateObjectActivation();
                m_ObjectList[iObject].Update(_fDeltaTime);
            }

            // Check for collisions
            m_CollisionManager.CheckCollisions();

            // Resolve collisions
            m_CollisionManager.ApplyCorrections();
        }

        #endregion Update
    }
}
